package com.ruoyi.project.business.punch.service.impl;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.project.business.monthschedule.service.IMonthScheduleService;
import com.ruoyi.project.business.punch.entity.ScheduleClockPunch;
import com.ruoyi.project.business.punch.mapper.ScheduleClockPunchMapper;
import com.ruoyi.project.business.punch.request.AddClockPunchRequest;
import com.ruoyi.project.business.punch.request.PunchOfDayRequest;
import com.ruoyi.project.business.punch.response.PunchOfDayResponse;
import com.ruoyi.project.business.punch.response.PunchResponse;
import com.ruoyi.project.business.punch.service.IScheduleClockPunchService;
import com.ruoyi.project.business.punch.vo.SaveOrUpdateVO;
import com.ruoyi.project.business.punchhelp.request.SaveOrUpdatePunchRequest;
import com.ruoyi.project.business.punchhelp.response.PunchWithUserResponse;
import com.ruoyi.project.system.entity.SysUser;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 打卡Service业务层处理
 * 
 * @author thanks
 * @date 2024-02-05
 */
@Service
public class ScheduleClockPunchServiceImpl implements IScheduleClockPunchService {
    @Autowired
    private ScheduleClockPunchMapper scheduleClockPunchMapper;
    @Autowired
    private IMonthScheduleService monthScheduleService;

    /**
     * 查询打卡
     * 
     * @param id 打卡ID
     * @return 打卡
     */
    @Override
    public ScheduleClockPunch selectScheduleClockPunchById(Long id) {
        return scheduleClockPunchMapper.selectScheduleClockPunchById(id);
    }

    /**
     * 查询打卡列表
     * 
     * @param scheduleClockPunch 打卡
     * @return 打卡
     */
    @Override
    public List<ScheduleClockPunch> selectScheduleClockPunchList(ScheduleClockPunch scheduleClockPunch) {
        return scheduleClockPunchMapper.selectScheduleClockPunchList(scheduleClockPunch);
    }

    /**
     * 新增打卡
     * 
     * @param scheduleClockPunch 打卡
     * @return 结果
     */
    @Override
    public int insertScheduleClockPunch(ScheduleClockPunch scheduleClockPunch) {
        scheduleClockPunch.setCreateTime(DateUtils.getNowDate());
        return scheduleClockPunchMapper.insertScheduleClockPunch(scheduleClockPunch);
    }

    /**
     * 修改打卡
     * 
     * @param scheduleClockPunch 打卡
     * @return 结果
     */
    @Override
    public int updateScheduleClockPunch(ScheduleClockPunch scheduleClockPunch) {
        scheduleClockPunch.setUpdateTime(DateUtils.getNowDate());
        return scheduleClockPunchMapper.updateScheduleClockPunch(scheduleClockPunch);
    }

    /**
     * 批量删除打卡
     * 
     * @param ids 需要删除的打卡ID
     * @return 结果
     */
    @Override
    public int deleteScheduleClockPunchByIds(Long[] ids) {
        return scheduleClockPunchMapper.deleteScheduleClockPunchByIds(ids);
    }

    /**
     * 删除打卡信息
     * 
     * @param id 打卡ID
     * @return 结果
     */
    @Override
    public int deleteScheduleClockPunchById(Long id) {
        return scheduleClockPunchMapper.deleteScheduleClockPunchById(id);
    }

    @Override
    public void fillPunchDatas(List<PunchWithUserResponse> punches, Date yearMonth, Long tenantId) {
        List<Long> userIds = punches.stream().map(PunchWithUserResponse::getUserId).distinct().collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(userIds)) {
            Integer year = DateUtils.year(yearMonth);
            Integer month = DateUtils.month(yearMonth);

            List<ScheduleClockPunch> list = scheduleClockPunchMapper.selectPunchListByUserId(year, month, userIds, tenantId);
//            List<PunchResponse> list = BeanUtils.copyList(list, PunchResponse.class);
            Map<Long, List<ScheduleClockPunch>> groupByUserIdMap = list.stream().collect(Collectors.groupingBy(ScheduleClockPunch::getUserId));

            punches.forEach((tmp) -> {
                List<PunchResponse> punchesComplete = new ArrayList<>();
                List<ScheduleClockPunch> punchesOfUser = groupByUserIdMap.get(tmp.getUserId());
                if(CollectionUtils.isNotEmpty(punchesOfUser)) {
                    // 再按天 dayOfMonth 分组
                    Map<Integer, List<ScheduleClockPunch>> groupByUserIdAndDayOfMonthMap =
                            punchesOfUser.stream().collect(Collectors.groupingBy(ScheduleClockPunch::getDayOfMonth));


                    groupByUserIdAndDayOfMonthMap.forEach((dayOfMonth, punchesOfDay) -> {
                        PunchResponse complete = new PunchResponse();

                        List<PunchOfDayResponse> punchesOfDayRes = BeanUtils.copyList(punchesOfDay, PunchOfDayResponse.class);
                        complete.setDayOfMonth(dayOfMonth);
                        complete.setPunchesOfDay(punchesOfDayRes);
                        punchesComplete.add(complete);
                    });
                }
                tmp.setPunches(punchesComplete);
            });
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdate(SaveOrUpdatePunchRequest request, Long operatorId, Long tenantId) {
        Long userId = request.getUserId();// 员工id
        Date yearMonth = request.getYearMonth();
        Integer dayOfMonth = request.getDayOfMonth();
        Integer year = DateUtils.year(yearMonth);
        Integer month = DateUtils.month(yearMonth);
        Long deptId = request.getDeptId();
        Date now = new Date();
        List<PunchOfDayRequest> punchesOfDay = request.getPunchesOfDay();

        SaveOrUpdateVO saveOrUpdateVO = parseParams(operatorId, tenantId, punchesOfDay, year, month, dayOfMonth, now, userId);
        if(saveOrUpdateVO != null) {
            List<ScheduleClockPunch> toAddList = saveOrUpdateVO.getToAddList();
            this.insertBatch(toAddList);

            List<ScheduleClockPunch> toEditList = saveOrUpdateVO.getToEditList();
            updateBatch(toEditList);

            List<Long> toDeleteIds = saveOrUpdateVO.getToDeleteList();
            deleteBatch(toDeleteIds);
        }
    }

    @Override
    public int punchClock(AddClockPunchRequest request, SysUser operator, Long tenantId) {

        LocalDate now = LocalDate.now();
        Long userId = operator.getUserId();
        Integer month = now.getMonthValue();
        Integer year = now.getYear();
        Integer dayOfMonth = now.getDayOfMonth();
        Integer dayOfWeek = now.getDayOfWeek().getValue();

        ScheduleClockPunch exist = this.scheduleClockPunchMapper.selectExist(year, month, dayOfMonth, tenantId);
        if(exist != null) {
            if(exist.getClockTimeStart() == null) {
                exist.setClockTimeStart(request.getClockTimeStart());
            }
            if (exist.getClockTimeEnd() == null) {
                exist.setClockTimeEnd(request.getClockTimeEnd());
            }
            exist.setUpdater(userId);
            exist.setUpdateTime(DateUtils.getNowDate());
            int count = this.scheduleClockPunchMapper.updateScheduleClockPunch(exist);
            return count;
        } else {
            exist = new ScheduleClockPunch();
            exist.setUserId(userId);
            exist.setClockTimeStart(request.getClockTimeStart());
            exist.setClockTimeEnd(request.getClockTimeEnd());
            exist.setPunchSort(1);
            exist.setDeleted(Boolean.FALSE);
            exist.setMonth(month);
            exist.setYear(year);
            exist.setDayOfMonth(dayOfMonth);
            exist.setDayOfWeek(dayOfWeek);
            exist.setCreateTime(DateUtils.getNowDate());
            exist.setCreator(userId);
            exist.setTenantId(tenantId);
            int count = this.scheduleClockPunchMapper.insertScheduleClockPunch(exist);
            return count;
        }



    }

    @Override
    public ScheduleClockPunch queryByDate(LocalDate date, Long userId, Long tenantId) {
        LocalDate now = LocalDate.now();
        Integer month = now.getMonthValue();
        Integer year = now.getYear();
        Integer dayOfMonth = now.getDayOfMonth();

        ScheduleClockPunch exist = this.scheduleClockPunchMapper.selectExist(year, month, dayOfMonth, tenantId);
        return exist;
    }

    private void deleteBatch(List<Long> ids) {
        if(CollectionUtils.isNotEmpty(ids)) {
            Long[] idArr = ids.toArray(new Long[ids.size()]);
            this.scheduleClockPunchMapper.deleteScheduleClockPunchByIds(idArr);
        }
    }

    private void updateBatch(List<ScheduleClockPunch> toEditList) {
        toEditList.forEach(entity -> {
            this.updateScheduleClockPunch(entity);
        });
    }

    private void insertBatch(List<ScheduleClockPunch> toAddList) {
        if(CollectionUtils.isNotEmpty(toAddList)) {
            scheduleClockPunchMapper.insertBatch(toAddList);
        }

    }

    /**
     * 其实是某一天的四个班段
     * @param operatorId
     * @param tenantId
     * @param punchesOfDay
     * @param year
     * @param month
     * @param now
     * @param userId
     * @return
     */
    private SaveOrUpdateVO parseParams(
            Long operatorId, Long tenantId, List<PunchOfDayRequest> punchesOfDay,
            Integer year, Integer month, Integer dayOfMonth, Date now, Long userId) {
        SaveOrUpdateVO saveOrUpdate = null;
        if(CollectionUtils.isNotEmpty(punchesOfDay)) {
            LocalDate date = LocalDate.of(year, month, dayOfMonth);
            int dayOfWeek = date.getDayOfWeek().getValue();
//            this.deleteScheduleClockPunchByDay(year, month, dayOfMonth, userId, operatorId, tenantId);
            // 校验 用户在 这天的排班表
            monthScheduleService.checkSchedule(userId, year, month, dayOfMonth, tenantId);

            saveOrUpdate = new SaveOrUpdateVO();
            List<ScheduleClockPunch> toAddList = new ArrayList<>();
            List<ScheduleClockPunch> toEditList = new ArrayList<>();
            List<Long> toDeleteList = new ArrayList<>();

            for (PunchOfDayRequest punchOfDayRequest : punchesOfDay) {
                int i = punchesOfDay.indexOf(punchOfDayRequest);
                if(i < 4) {

                    LocalTime clockTimeStart = punchOfDayRequest.getClockTimeStart();
                    LocalTime clockTimeEnd = punchOfDayRequest.getClockTimeEnd();
//                    Date start = DateUtils.transferTime(date, clockTimeStart);
//                    Date end = DateUtils.transferTime(date, clockTimeEnd);
                    Integer punchSort = punchOfDayRequest.getPunchSort();

                    ScheduleClockPunch entity = new ScheduleClockPunch();
                    entity.setClockTimeStart(clockTimeStart);
                    entity.setClockTimeEnd(clockTimeEnd);
                    entity.setPunchSort(punchSort);
                    entity.setDayOfMonth(dayOfMonth);
                    entity.setYear(year);
                    entity.setMonth(month);
                    entity.setDayOfWeek(dayOfWeek);
                    entity.setDeleted(Boolean.FALSE);
                    entity.setUpdater(operatorId);
                    entity.setUpdateTime(now);

                    Long id = punchOfDayRequest.getId();
                    if(id != null) {
                        entity.setId(id);
                        if((clockTimeStart == null && clockTimeEnd == null)) {
                            toDeleteList.add(id);
                        } else {
                            // 更新
                            toEditList.add(entity);
                        }
                    } else {
                        // 新增
                        entity.setCreator(operatorId);
                        entity.setCreateTime(now);
                        entity.setUserId(userId);
                        entity.setTenantId(tenantId);
                        toAddList.add(entity);
                    }
                }
            }
            saveOrUpdate.setToAddList(toAddList);
            saveOrUpdate.setToEditList(toEditList);
            saveOrUpdate.setToDeleteList(toDeleteList);
        }
        return saveOrUpdate;
    }

    private void deleteScheduleClockPunchByDay(Integer year, Integer month, int dayOfMonth, Long userId, Long operatorId, Long tenantId) {
        List<ScheduleClockPunch> scheduleClockPunches = this.scheduleClockPunchMapper.selectPunchListByUserId(year, month, Arrays.asList(userId), tenantId);
        if(CollectionUtils.isNotEmpty(scheduleClockPunches)) {
            List<Long> ids = scheduleClockPunches.stream()
                    .filter(tmp -> tmp.getDayOfMonth() != null && dayOfMonth == tmp.getDayOfMonth().intValue())
                    .map(ScheduleClockPunch::getId).collect(Collectors.toList());
            Long[] idArr = ids.toArray(new Long[ids.size()]);
            if(ArrayUtils.isNotEmpty(idArr)) {
                this.scheduleClockPunchMapper.deleteScheduleClockPunchByIds(idArr);
            }
        }
    }
}
